package com.evil.common.file.util;

import com.evil.common.core.enums.RCodeEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.file.dto.xml.XmlNodeTreeRespDTO;
import lombok.extern.slf4j.Slf4j;
import org.w3c.dom.*;
import org.xml.sax.SAXException;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @author liyang
 * @date 2023-04-14 11:09
 */
@Slf4j
public class XmlUtil {

    private static final String XML_DOCTYPE_REG = "<!DOCTYPE .+?>";

    /**
     * 解析XML文件
     *
     * @param bytes xml 文件
     * @return XmlNodeRespDTO 树
     */
    public static List<XmlNodeTreeRespDTO> analysisXmlFile(byte[] bytes) {
        String s = new String(bytes, StandardCharsets.UTF_8);
        s = s.replaceAll(XML_DOCTYPE_REG, "");
        ByteArrayInputStream bIs = new ByteArrayInputStream(s.getBytes(StandardCharsets.UTF_8));
        Document document;
        try {
            document = cn.hutool.core.util.XmlUtil.createDocumentBuilder().parse(bIs);
        } catch (SAXException | IOException e) {
            log.error("解析XML文件失败：", e);
            throw new BusinessException(RCodeEnum.ERROR_RUNTIME, "解析XML文件失败");
        }
        Element element = document.getDocumentElement();
        return recursionXmlNode("", element.getChildNodes());
    }

    /**
     * 递归xml节点
     *
     * @param level      level
     * @param childNodes childNodes
     */
    private static List<XmlNodeTreeRespDTO> recursionXmlNode(String level, NodeList childNodes) {
        List<XmlNodeTreeRespDTO> nodes = new ArrayList<>();
        for (int i = 0, l = childNodes.getLength(); i < l; i++) {
            Node node = childNodes.item(i);
            if (node.getNodeType() != 3 && node.getNodeType() != 8) {
                Map<String, String> attributeMap = xmlNodeAttributeMap(node.getAttributes());
                log.info("{} node.nodeName:{}, nodeType:{}, attributeMap:{}", level, node.getNodeName(), node.getNodeType(), attributeMap);

                XmlNodeTreeRespDTO xmlNodeTreeRespDTO = new XmlNodeTreeRespDTO();
                xmlNodeTreeRespDTO.setNodeName(node.getNodeName());
                xmlNodeTreeRespDTO.setNodeValue(node.getNodeValue());
                xmlNodeTreeRespDTO.setNodeAttributeMap(attributeMap);
                // 递归子级
                xmlNodeTreeRespDTO.setChildren(recursionXmlNode(level + "--", node.getChildNodes()));

                nodes.add(xmlNodeTreeRespDTO);
            }
        }
        return nodes;
    }

    /**
     * 获取xml节点属性map
     *
     * @param attributes attributes
     * @return Map
     */
    private static Map<String, String> xmlNodeAttributeMap(NamedNodeMap attributes) {
        LinkedHashMap<String, String> attributeMap = new LinkedHashMap<>();
        if (null != attributes) {
            for (int i = 0, l = attributes.getLength(); i < l; i++) {
                Node item = attributes.item(i);
                attributeMap.put(item.getNodeName(), item.getNodeValue());
            }
        }
        return attributeMap;
    }
}
